/**
 * @(#) Robot.java
 */

package pcsr.hwVirtualization;

import java.util.*;

import pcsr.exceptions.PCSRActuatorRuntimeException;
import pcsr.exceptions.PCSRRobotRuntimeException;

/**
 * Represents a virtualized robot, managed by PCSR.
 * @author Paulo Fagundes
 *
 */
public class Robot implements IRobotObservable, IRobot, IRobotDriver, IRobotDriverObserver {
	/**
	 * Might be the same as driverObserver, if driver is IRobotDriverObservable
	 */
	private IDriverActions driverActions;
	
	private IRobotDriverObservable driverObserver;

	private Map<String,Actuator> robotActuators;

	private RobotInfo robotInfo;

	private List<IRobotObserver> robotObservers;

	private Map<String,Sensor> robotSensors;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void ActuatorChanged( Actuator newActuatorStatus ) {

		try
		{
			Actuator changedActuator = robotActuators.get(newActuatorStatus
					.GetActuatorID());
			
			if (changedActuator != null) {
				// If any change occurred, notify a change on the robot.
				if (AnalizeActuatorChange(changedActuator, newActuatorStatus)) {
					if (robotObservers != null) {
						// TODO Check if any additional notification is required.
					}
				}
			}
		}
		catch (Exception re) {
			throw new PCSRRobotRuntimeException(re);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Collection<Actuator> GetAllActuators( ) {
		try
		{
			return robotActuators.values();
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Collection<Sensor> GetAllSensors( ) {
		try
		{
			return robotSensors.values();
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public RobotInfo GetRobotInfo( ) {
		try
		{
			return this.robotInfo;
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Robot GetRobotStatus( ) {
		try
		{
			return (Robot) this.clone();
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * Initializes the instance of {@code Robot}.
	 */
	public void Initialize()
	{
		try
		{
			for(Actuator a :  driverActions.GetAllActuators())
			{
				robotActuators.put(a.GetActuatorID(), a);
				a.setRobotDriver(this);
			}
			for(Sensor s : driverActions.GetAllSensors())
			{
				robotSensors.put(s.GetSensorID(), s);
				s.setRobotDriver(this);
			}
			
			robotInfo = driverActions.GetRobotInfo();
		}
		catch (RuntimeException re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean ObserveRobot( IRobotObserver observer ) {
		try
		{
			return robotObservers.add(observer);
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void SensorChanged( Sensor newSensorStatus ) {
		try
		{
			Sensor changedSensor = robotSensors.get(newSensorStatus.GetSensorID());
	
			if (changedSensor != null) {
				// If any change occurred, notify a change on the robot.
				if (AnalizeSensorChange(changedSensor, newSensorStatus)) {
					if (robotObservers != null) {
						// TODO Check if any additional notification is required.
					}
				}
			}
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void SetActuatorStatusToDriver( Actuator actuator ) {
		try
		{
			driverActions.SetActuatorStatus(actuator);
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void SetSensorStatusToDriver( Sensor sensor ) {
		try
		{
			driverActions.SetSensorStatus(sensor);
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean UnObserveRobot( IRobotObserver observer ) {
		try
		{
			return robotObservers.remove(observer);
		}
		catch (Exception re) {
			throw new PCSRActuatorRuntimeException(re);
		}
	}

	/**
	 * Analyzes if a change in the Actuator is enough to generate a
	 * notification. If enough, notifies all observers on the corresponding
	 * Actuator.
	 * 
	 * @param changedActuator
	 * The new status of the Actuator.
	 * @param newActuatorStatus
	 * The current status of the Actuator.
	 * @return True if any change is to be notified. False otherwise.
	 */
	private boolean AnalizeActuatorChange( Actuator changedActuator, Actuator newActuatorStatus ) {
		boolean ret = false;
		// TODO Define if robot filter changes to Actuators
		if(!changedActuator.GetActuatorValue().equals(newActuatorStatus.GetActuatorValue()))
		{
			changedActuator.SetValue(newActuatorStatus.GetActuatorValue());
			ret = true;			
		}
		if(!changedActuator.GetOperationMode().equals(newActuatorStatus.GetOperationMode()))
		{
			changedActuator.SetOperationMode(newActuatorStatus.GetOperationMode());
			ret = true;			
		}
		return ret;
	}


	/**
	 * Analyzes if a change in the Sensor is enough to generate a notification.
	 * If enough, notifies all observers on the corresponding Sensor.
	 * 
	 * @param changedSensor
	 * @param newSensorStatus
	 * @return True if any change is to be notified. False otherwise.
	 */
	private boolean AnalizeSensorChange( Sensor changedSensor, Sensor newSensorStatus ) {
		boolean ret = false;
		// TODO Define if robot filter changes to Sensors
		if(!changedSensor.GetSensorValue().equals(newSensorStatus.GetSensorValue()))
		{
			changedSensor.SetValue(newSensorStatus.GetSensorValue());
			ret = true;			
		}
		if(!changedSensor.GetOperationMode().equals(newSensorStatus.GetOperationMode()))
		{
			changedSensor.SetMode(newSensorStatus.GetOperationMode());
			ret = true;			
		}
		return ret;
	}

	/**
	 * Loads the driver defined in configuration file.
	 */
	private void LoadDriver( )
	{
		// If Driver is IRobotDriverObservable
			// Initiate driver variable
		// If Driver is IRobotController
			// Create instance of RobotController to update me.
		IDriverActions thedriver = DriverFactory.createInstance();
		driverActions = thedriver;
		driverActions.Initialize();
		if(thedriver instanceof IRobotDriverObservable)
		{
			driverObserver = (IRobotDriverObservable) thedriver;
			driverObserver.ObserverRobotDriver(this);
		}
		if(thedriver instanceof IRobotController)
		{
			RobotController updateThread = new RobotController(this,(IRobotController) thedriver);
			updateThread.run();
		}
		
		
	}

	{
		robotObservers = new LinkedList<IRobotObserver>();
		robotActuators = new Hashtable<String, Actuator>();
		robotSensors = new Hashtable<String, Sensor>();
		LoadDriver();
	}
	
	
}
